package net.wzw.redis;

import lombok.extern.slf4j.Slf4j;
import net.wzw.utils.CommonUtil;
import net.wzw.utils.JsonUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @className: redisCache
 * @author: wzw，
 * @date: 2025-04-14 22:38
 * @Version: 1.0
 * @description: redis 缓存封装类，方便后续使用
 */
@Slf4j
@Component
public class RedisCache {

    private RedisTemplate redisTemplate;

    public RedisCache(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 缓存空数据
     */
    public static final String EMPTY_CACHE = "{}";

    /**
     * 分布式锁加锁时间 200 毫秒
     */
    public static final long USER_UPDATE_LOCK_TIMEOUT = 200;

    /**
     * 两天有效期
     */
    public static final Integer TWO_DAYS_SECONDS = 2 * 24 * 60 * 60;

    /**
     * 1 小时
     */
    public static final Integer ONE_HOURS_SECONDS = 1 * 60 * 60;

    /**
     * 生成缓存穿透过期时间，单位 秒
     * @return
     */
    public static Integer generateCachePenetrationExpire() {
        return CommonUtil.genRandomInt(30, 100);
    }


    /**
     * 生成缓存过期时间：2天加上随机几小时，防止缓存惊群，穿透数据库
     * @return
     */
    public static Integer generateCacheExpire() {
        return TWO_DAYS_SECONDS + CommonUtil.genRandomInt(0, 10) * 60 * 60;
    }

    /**
     * 写入缓存
     * @param key
     * @param value
     * @param seconds 设置过期时间
     */
    public void set(String key, String value, int seconds) {
        ValueOperations<String,String> op = redisTemplate.opsForValue();
        if (seconds > 0) {
            // 设置缓存时间
            op.set(key, value, seconds, TimeUnit.SECONDS);
        } else {
            op.set(key, value);
        }
    }

    /**
     *  写入缓存 value 为 Object 类型
     *  TODO 后续会增加热 Key 检测
     * @param key
     * @param value
     * @param seconds
     */
    public void setCache(String key, Object value, int seconds) {
        // 先将 Object 转成 Json 字符串再进行存储
        this.set(key, JsonUtil.object2Json(value), seconds);
    }

    /**
     * 读取缓存
     * @param key
     * @return
     */
    public String get(String key) {
        ValueOperations<String,String> op = redisTemplate.opsForValue();
        return op.get(key);
    }

    /**
     * 读取缓存 value 为int类型
     * @param key
     * @return
     */
    public Integer getInt(String key) {
        ValueOperations<String,Integer> op = redisTemplate.opsForValue();
        return op.get(key);
    }

    /**
     * 读取缓存
     * TODO 后续会增加热 Key 读取，如果存在优先从内存中读取，否则再读取 Redis 缓存
     * @param key
     * @return
     */
    public Object getCache(String key) {
        String value = this.get(key);
        log.info("缓存模块-从缓存中获取信息,key:{},value:{}", key, value);
        return value;
    }

    /**
     * 删除缓存
     * @param key
     * @return
     */
    public boolean delete(String key){
        return redisTemplate.delete(key);
    }

    /**
     * 写入缓存并设置过期时间
     * @param key
     * @param value
     * @param time
     */
    public void setex(String key, String value, long time) {
        redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
    }

    /**
     * 批量获取缓存
     * @param keyList
     * @return
     */
    public List mget(List<String> keyList) {
        return redisTemplate.opsForValue().multiGet(keyList);
    }

    /**
     * 删除 Set 集合中 key 下的多个值
     * @param key
     * @param values
     */
    public void srem(String key, String[] values) {
        redisTemplate.opsForSet().remove(key, values);
    }

    /**
     * 添加 Set 集合中 key 下的多个值
     * @param key
     * @param values
     */
    public void sadd(String key, String[] values) {
        redisTemplate.opsForSet().add(key, values);
    }

    /**
     * 获取 Set 集合成员
     * @param key
     * @return
     */
    public Set smembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * Set 集合成员是否存在
     * @param key
     * @param member
     * @return
     */
    public Boolean sismember(String key, String member) {
        return redisTemplate.opsForSet().isMember(key, member);
    }

    /**
     * ZSet 集合有序区间值
     * @param key
     * @param min
     * @param max
     * @param offset
     * @param count
     * @return
     */
    public Set zrangeByScore(String key, double min, double max, long offset, long count) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max, offset, count);
    }

    /**
     * ZSet 集合有序存储
     * @param key
     * @param member
     * @param score
     * @return java.lang.Boolean
     */
    public Boolean zadd(String key, String member, double score) {
        return redisTemplate.opsForZSet().add(key, member, score);
    }

    /**
     * ZSet 集合有序删除
     *
     * @param key
     * @param values
     * @return java.lang.Long
     */
    public Long zremove(String key, String... values) {
        return redisTemplate.opsForZSet().remove(key, values);
    }

    /**
     * ZSet 集合有序数量
     * @param key
     * @return java.lang.Long
     */
    public Long zcard(String key) {
        return redisTemplate.opsForZSet().zCard(key);
    }


    /**
     * 判断 hash key 是否存在
     * @param key
     * @return
     */
    public boolean hExists(String key) {
        return hGetAll(key).isEmpty();
    }

    /**
     * 判断 hash field 是否存在
     * @return
     */
    public boolean hFieldExists(String key, String field) {
        return redisTemplate.opsForHash().hasKey(key, field);
    }

    /**
     * 获取 hash 变量中的键值对
     * 对应 redis hgetall 命令
     * @param key
     * @return
     */
    public Map<String, String> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 获取 hash 变量中的
     * @return
     */
    public Object hGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }


    /**
     * hash 变量中 field 对应的 value 自增
     * @param key
     * @param field
     * @param increment
     * @return
     */
    public Long hIncr(String key, String field, Integer increment) {
        return redisTemplate.opsForHash().increment(key, field, increment);
    }

    /**
     * hash 变量 field 对应的 value 自增
     * @param key
     * @param field
     * @return
     */
    public Long hIncr(String key, String field) {
        return hIncr(key, field, 1);
    }

    /**
     * 获取 hash 变量中的 field 数量
     * 对应 redis hlen 命令
     * @param key
     * @return
     */
    public Long hLen(String key) {
        return redisTemplate.opsForHash().size(key);
    }

    /**
     * 添加 hash 的 value
     * @param key
     * @param field
     * @param value
     */
    public void hPut(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    /**
     * 以 map 集合的形式添加 hash 键值对
     * @param key
     * @param map
     */
    public void hPutAll(String key, Map<String, String> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 删除某个 field
     * @param key
     * @param field
     */
    public long hDel(String key, String field) {
        return redisTemplate.opsForHash().delete(key, field);
    }

    /**
     * 设置 key 过期时间
     * @param key
     * @param time
     * @param unit
     */
    public void expire(String key, long time, TimeUnit unit) {
        redisTemplate.expire(key, time, unit);
        log.info("缓存更新过期时间，key：{}，time：{}", key, time);
    }

    /**
     * 以 list 集合的形式添加数据
     * @param key
     * @return
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 以 list 集合的形式添加数据
     * @param key
     * @param values
     * @return
     */
    public Long lPushAll(String key, String... values) {
        return redisTemplate.opsForList().leftPushAll(key, values);
    }

    /**
     * 以 list 集合的形式添加数据
     * @param key
     * @param values
     * @return
     */
    public Long lPushAll(String key, List<String> values) {
        return redisTemplate.opsForList().leftPushAll(key, values);
    }

    /**
     * 以 list 集合的形式添加数据
     * @param key
     * @param values
     * @return
     */
    public Long rPushAll(String key, String... values) {
        return redisTemplate.opsForList().rightPushAll(key, values);
    }

    /**
     * 以 list 集合的形式添加数据
     * @param key
     * @param values
     * @return
     */
    public Long rPushAll(String key, List<String> values) {
        return redisTemplate.opsForList().rightPushAll(key, values);
    }

    /**
     * 返回 list 集合下表区间的元素
     * @param key
     * @param start
     * @param end
     * @return
     */
    public List<String> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 返回 list 集合的大小
     * @param key
     * @return
     */
    public Long lsize(String key) {
        return redisTemplate.opsForList().size(key);
    }

    /**
     * 设置缓存过期时间
     * @param key
     * @return
     */
    public void expire(String key, long time) {
        this.expire(key, time, TimeUnit.SECONDS);
    }

    /**
     * 执行 lua 脚本
     * @param script
     * @param keys
     * @param args
     * @param <T>
     * @return
     */
    public <T> T execute(RedisScript<T> script, List<String> keys, String... args) {
        return (T) redisTemplate.execute(script, keys, args);
    }

    /**
     * 写入缓存并设置过期时间
     * @param key
     * @param value
     * @param time
     * @param timeUnit
     * @return void
     */
    public void setex(String key, String value, long time, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, time, timeUnit);
    }

    /**
     * 递增
     * @param key
     * @param increment
     * @return
     */
    public Long increment(String key, long increment) {
        return redisTemplate.opsForValue().increment(key, increment);
    }

    /**
     * 递减
     * @param key
     * @param decrement
     * @return
     */
    public Long decrement(String key, long decrement) {
        return redisTemplate.opsForValue().decrement(key, decrement);
    }

    /**
     * 获取缓存失效时间
     * @param key
     * @param timeUnit
     * @return
     */
    public Long getExpire(String key, TimeUnit timeUnit) {
        return redisTemplate.getExpire(key, timeUnit);
    }

    /**
     * 指定缓存失效时间
     * @param key
     * @param date
     * @return
     */
    public Boolean expireAt(String key, Date date) {
        return redisTemplate.expireAt(key, date);
    }

    /**
     * 获取 hash 多 field 值
     * @param key
     * @param list
     * @return
     */
    public List<String> multiGet(String key, List<String> list) {
        return redisTemplate.opsForHash().multiGet(key, list);
    }

    /**
     * 新增 hash 值
     * @param key
     * @param field
     * @param value
     * @return
     */
    public Boolean putIfAbsent(String key, String field, String value) {
        return redisTemplate.opsForHash().putIfAbsent(key, field, value);
    }
}
